Three equal parts [Equal Ones]¶
Time: O(N); Space: O(1); hard
Given an array A of 0s and 1s, divide the array into 3 non-empty parts such that all of these parts represent the same binary value.
If it is possible, return any [i, j] with i+1 < j, such that: * A[0], A[1], …, A[i] is the first part; * A[i+1], A[i+2], …, A[j-1] is the second part, and * A[j], A[j+1], …, A[A.length - 1] is the third part. * All three parts have equal binary value.
If it is not possible, return [-1, -1].
Note that the entire part is used when considering what binary value it represents. For example, [1,1,0] represents 6 in decimal, not 3. Also, leading zeros are allowed, so [0,1,1] and [1,1] represent the same value.
Example 1:
Input: A = [1,0,1,0,1]
Output: [0,3]
Example 2:
Input: A = [1,1,0,1,1]
Output: [-1,-1]
Notes:
3 <= A.length <= 30000
A[i] == 0 or A[i] == 1
1. Equal Ones¶
Intuition Each part has to have the same number of ones in their representation. The algorithm given below is the natural continuation of this idea.
Algorithm Say S is the number of ones in A. Since every part has the same number of ones, they all should have T = S / 3 ones. If S isn’t divisible by 3, the task is impossible.
We can find the position of the 1st, T-th, T+1-th, 2T-th, 2T+1-th, and 3T-th one. The positions of these ones form 3 intervals: [i1, j1], [i2, j2], [i3, j3]. (If there are only 3 ones, then the intervals are each length 1.)
Between them, there may be some number of zeros. The zeros after j3 must be included in each part: say there are z of them (z = S.length - j3). So the first part, [i1, j1], is now [i1, j1+z]. Similarly, the second part, [i2, j2], is now [i2, j2+z].
If all this is actually possible, then the final answer is [j1+z, j2+z+1].
[1]:
class Solution1(object):
"""
Time: O(N), where N is the length of A.
Space: O(N).
"""
def threeEqualParts(self, A):
"""
:type A: List[int]
:rtype: List[int]
"""
IMP = [-1, -1]
S = sum(A)
if S % 3:
return IMP
T = S / 3
if T == 0:
return [0, len(A) - 1]
breaks = []
su = 0
for i, x in enumerate(A):
if x:
su += x
if su in {1, T+1, 2*T+1}:
breaks.append(i)
if su in {T, 2*T, 3*T}:
breaks.append(i)
i1, j1, i2, j2, i3, j3 = breaks
# The array is in the form W [i1, j1] X [i2, j2] Y [i3, j3] Z
# where [i1, j1] is a block of 1s, etc.
if not(A[i1:j1+1] == A[i2:j2+1] == A[i3:j3+1]):
return [-1,-1]
# x, y, z: the number of zeros after part 1, 2, 3
x = i2 - j1 - 1
y = i3 - j2 - 1
z = len(A) - j3 - 1
if x < z or y < z:
return IMP
j1 += z
j2 += z
return [j1, j2+1]
[3]:
s = Solution1()
A = [1, 0, 1, 0, 1]
assert s.threeEqualParts(A) == [0, 3]
A = [1, 1, 0, 1, 1]
assert s.threeEqualParts(A) == [-1,-1]
[4]:
class Solution2(object):
"""
Time: O(N)
Space: O(1)
"""
def threeEqualParts(self, A):
"""
:type A: List[int]
:rtype: List[int]
"""
total = sum(A)
if total % 3 != 0:
return [-1, -1]
if total == 0:
return [0, len(A)-1]
count = total // 3
nums = [0]*3
c = 0
for i in range(len(A)):
if A[i] == 1:
if c % count == 0:
nums[c // count] = i
c += 1
while nums[2] != len(A):
if not A[nums[0]] == A[nums[1]] == A[nums[2]]:
return [-1, -1]
nums[0] += 1
nums[1] += 1
nums[2] += 1
return [nums[0]-1, nums[1]]
[5]:
s = Solution2()
A = [1, 0, 1, 0, 1]
assert s.threeEqualParts(A) == [0, 3]
A = [1, 1, 0, 1, 1]
assert s.threeEqualParts(A) == [-1,-1]